using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;

namespace FontManager
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        MainForm PARENT_FORM;
        System.Windows.Forms.Control GAME_FORM;

        public IntPtr DRAW_SURFACE;
        public CustomPictureBox SURFACE_PICTURE_BOX;

        public XNAHelper XNA_MAIN_FORM_HELPER;
        public Texture2D ICON_BACKGROUND;
        public Texture2D ICON_LINES;
        public Texture2D ICON_MAXIMIZE;
        public Texture2D ICON_TILES;
        public Texture2D ICON_ZOOM_IN;
        public Texture2D ICON_ZOOM_OUT;
        public Texture2D ICON_ZOOM_ORIGIN;

        /// <summary>
        /// winforms in XNA approach. Using picture box as an drawing surface for xna.
        /// </summary>
        /// <param _name="drawSurface">picture box handler</param>
        /// <param _name="parentForm">parent form</param>
        /// <param _name="surfaceBox">handler owner</param>
        public Game1(IntPtr drawSurface, MainForm parentForm, CustomPictureBox surfaceBox)
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            this.IsMouseVisible = false;
            this.DRAW_SURFACE = drawSurface;
            this.PARENT_FORM = parentForm;
            this.SURFACE_PICTURE_BOX = surfaceBox;

            Mouse.WindowHandle = DRAW_SURFACE;
            GAME_FORM = System.Windows.Forms.Control.FromHandle(this.Window.Handle);
            graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
            
            GAME_FORM.VisibleChanged += new EventHandler(gameForm_VisibleChanged);
            GAME_FORM.SizeChanged += new EventHandler(gameForm_SizeChanged);
            SURFACE_PICTURE_BOX.SizeChanged += new EventHandler(gameForm_SizeChanged);
            SURFACE_PICTURE_BOX.MouseWheel += new System.Windows.Forms.MouseEventHandler(surfacePictureBox_MouseWheel);
            SURFACE_PICTURE_BOX.MouseMove += new System.Windows.Forms.MouseEventHandler(surfacePictureBox_MouseMove);
        }

        /// <summary>
        /// to change handler should:
        /// change handler;
        /// resize it;
        /// change surfaceBox;
        /// change what to draw;
        /// change what to update;
        /// keep switching those based on which handler is in use;
        /// </summary>
        /// <param _name="handler"></param>
        public void resetHandler(IntPtr handler)
        {
            PresentationParameters pp = GraphicsDevice.PresentationParameters;
            pp.DeviceWindowHandle = handler;

            GraphicsDevice.Reset(pp);
        }

        /// <summary>
        /// deals with the cursor change once the mouse if clicked on the mouse.
        /// </summary>
        /// <param _name="sender"></param>
        /// <param _name="e"></param>
        void surfacePictureBox_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (XNA_MAIN_FORM_HELPER.DRAW_LINE_DRAG_CURSOR)
            {
                SURFACE_PICTURE_BOX.Cursor = System.Windows.Forms.Cursors.Hand;
            }
            else
            {
                SURFACE_PICTURE_BOX.Cursor = System.Windows.Forms.Cursors.Default;
            }
        }

        #region GDI+ event listeners
        int mousedelta = 0;
        /// <summary>
        /// since XNA mouse wheel didn't gave me expected result I used customPictureBox mouseWheel event instead
        /// and passed its parameter to XNA.
        /// </summary>
        /// <param _name="sender"></param>
        /// <param _name="e"></param>
        void surfacePictureBox_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Delta > 0)
            {
                mousedelta = 1;
            }
            if (e.Delta < 0)
            {
                mousedelta = -1;
            }
        }

        void gameForm_SizeChanged(object sender, EventArgs e)
        {
            if (PARENT_FORM.WindowState != System.Windows.Forms.FormWindowState.Minimized)
            {
                graphics.PreferredBackBufferWidth = SURFACE_PICTURE_BOX.Width;
                graphics.PreferredBackBufferHeight = SURFACE_PICTURE_BOX.Height;
                graphics.ApplyChanges();
                if (XNA_MAIN_FORM_HELPER != null)
                {
                    XNA_MAIN_FORM_HELPER.reInitialize(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight);
                }
            }
        }

        void gameForm_VisibleChanged(object sender, EventArgs e)
        {
            if (GAME_FORM.Visible)
                GAME_FORM.Visible = false;
        }

        void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
        {
            e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = DRAW_SURFACE;
        }
        #endregion

        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            base.Initialize();
        }

        /// <summary>
        /// loads content including winForm buttons icons.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            XNA_MAIN_FORM_HELPER = new XNAHelper(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, GraphicsDevice);
            XNA_MAIN_FORM_HELPER.LoadContent(Content);
            PARENT_FORM.numericUpDown1.Value = (decimal)(XNAHelper.Alpha * 100);
            // TODO: use this.Content to load your game content here
            ICON_BACKGROUND = Content.Load<Texture2D>(@"Buttons\background");
            ICON_LINES = Content.Load<Texture2D>(@"Buttons\linesIcon");
            ICON_MAXIMIZE = Content.Load<Texture2D>(@"Buttons\maximize");
            ICON_TILES = Content.Load<Texture2D>(@"Buttons\virtualTiles");
            ICON_ZOOM_IN = Content.Load<Texture2D>(@"Buttons\zoomIN");
            ICON_ZOOM_OUT = Content.Load<Texture2D>(@"Buttons\zoomOUT");
            ICON_ZOOM_ORIGIN = Content.Load<Texture2D>(@"Buttons\zoomORIGIN");
            PARENT_FORM.initButtonImages();
        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        
        MouseState oldState;
        bool movingBackground = false;
        int squaresAcross = 0;
        int squaresDown = 0;
        /// <summary>
        /// based on lines state and if they are moving, if background is being draged 
        /// suspends some and runs other update methods.
        /// </summary>
        /// <param _name="gameTime"></param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            MouseState currentState = Mouse.GetState();
            //============PREVIEW LINES AND _characters=====================//
            if (!movingBackground)
            {
                XNA_MAIN_FORM_HELPER.Update(currentState, mousedelta);
                mousedelta = 0;
            }
            //============BACKGROUND=======================================//
            if (BACKGROUND != null && !XNA_MAIN_FORM_HELPER.DRAW_LINE_DRAG_CURSOR)
            {
                if (currentState.LeftButton == ButtonState.Pressed && !XNA_MAIN_FORM_HELPER.OVER_THE_LINE && MainForm.NEW_FONT.UseBackground)
                {
                    movingBackground = true;
                    BACKGROUND.X += currentState.X - oldState.X;
                    BACKGROUND.Y += currentState.Y - oldState.Y;
                }

                if (currentState.LeftButton == ButtonState.Released && oldState.LeftButton == ButtonState.Pressed)
                {
                    movingBackground = false;
                }
            }
            //================================TILES==================================//
            if (TILE != null && !movingBackground)
            {
                if (squaresAcross != graphics.PreferredBackBufferWidth / TILE.Texture2d.Width || squaresDown != graphics.PreferredBackBufferHeight / TILE.Texture2d.Height)
                {
                    squaresAcross = graphics.PreferredBackBufferWidth / TILE.Texture2d.Width;
                    squaresDown = graphics.PreferredBackBufferHeight / TILE.Texture2d.Height;
                }
            }
            base.Update(gameTime);

            oldState = currentState;
        }

        /// <summary>
        /// takes font background as graphicsDevice.Clean LineColor.
        /// if uses background draws background;
        /// if uses tiles draws tiles;
        /// draws character with lines.
        /// </summary>
        /// <param _name="gameTime"></param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            if (MainForm.NEW_FONT != null)
            {
                int nA = MainForm.NEW_FONT.Background.A;
                int nR = MainForm.NEW_FONT.Background.R;
                int nG = MainForm.NEW_FONT.Background.G;
                int nB = MainForm.NEW_FONT.Background.B;

                GraphicsDevice.Clear(new Color(nR, nG, nB, nA));
                //=========================BACKGROUND=============================//
                if (MainForm.NEW_FONT.UseBackground)
                {
                    DrawBackground(spriteBatch);
                }
                //==========================TILES==================================//
                if (MainForm.NEW_FONT.UseTileBackground)
                {
                    DrawTiles(spriteBatch);
                }
                //=====================PREVIEW LINES & CHARS========================//
                XNA_MAIN_FORM_HELPER.Draw(spriteBatch);
            }
            base.Draw(gameTime);
        }

        //=======================Background====================================//
        public CustomTexture2D BACKGROUND;
        /// <summary>
        /// initializes background from image URI passed from MainForm 
        /// </summary>
        /// <param _name="backgroundUri">image uri</param>
        public void initPreviewBackground(string backgroundUri)
        {
            BACKGROUND = new CustomTexture2D();
            BACKGROUND.Texture2d = XNA_MAIN_FORM_HELPER.getTextureFromFile(backgroundUri, GraphicsDevice);
            BACKGROUND.X = 0;
            BACKGROUND.Y = 0;
            BACKGROUND.Uri = backgroundUri;
        }

        /// <summary>
        /// straight forward draws a background.
        /// </summary>
        /// <param _name="spriteBatch"></param>
        public void DrawBackground(SpriteBatch spriteBatch)
        {
            spriteBatch.Begin();
            spriteBatch.Draw(BACKGROUND.Texture2d, BACKGROUND.Position, Color.White);
            spriteBatch.End();
        }

        //=======================Tiles====================================//
        public CustomTexture2D TILE;

        /// <summary>
        /// initializes tiles from image URI passed from MainForm 
        /// </summary>
        /// <param _name="backgroundUri">tile image uri</param>
        public void initPreviewTiles(string TileUri)
        {
            TILE = new CustomTexture2D();
            TILE.Texture2d = XNA_MAIN_FORM_HELPER.getTextureFromFile(TileUri, GraphicsDevice);
            TILE.X = 0;
            TILE.Y = 0;
            TILE.Uri = TileUri;
        }

        /// <summary>
        /// loops based on viewport and draws tiles map.
        /// </summary>
        /// <param _name="spriteBatch"></param>
        public void DrawTiles(SpriteBatch spriteBatch)
        {
            spriteBatch.Begin();

            for (int y = 0; y < squaresDown; y++)
            {
                for (int x = 0; x < squaresAcross; x++)
                {
                    spriteBatch.Draw(
                        TILE.Texture2d,
                        new Rectangle(x * TILE.Texture2d.Width, y * TILE.Texture2d.Height, TILE.Texture2d.Width, TILE.Texture2d.Height), 
                        new Rectangle(0, 0, TILE.Texture2d.Width, TILE.Texture2d.Height), 
                        Color.White);
                }
            }

            spriteBatch.End();
        }
    }
}
